/*-------------------------------------------------------------------------
 *
 * pg_shdepend.c
 *	  支持pg_shdepend关系操作的常规例程
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/backend/catalog/pg_shdepend.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres.h"

#include "access/genam.h"
#include "access/htup_details.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/catalog.h"
#include "catalog/dependency.h"
#include "catalog/indexing.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_collation.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_largeobject_metadata.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_shdepend.h"
#include "catalog/pg_statistic_ext.h"
#include "catalog/pg_subscription.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
#include "commands/alter.h"
#include "commands/collationcmds.h"
#include "commands/conversioncmds.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
#include "commands/policy.h"
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
#include "commands/subscriptioncmds.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "commands/typecmds.h"
#include "miscadmin.h"
#include "storage/lmgr.h"
#include "utils/acl.h"
#include "utils/fmgroids.h"
#include "utils/memutils.h"
#include "utils/syscache.h"

typedef enum
{
	LOCAL_OBJECT,
	SHARED_OBJECT,
	REMOTE_OBJECT
} SharedDependencyObjectType;

typedef struct
{
	ObjectAddress object;
	char		deptype;
	SharedDependencyObjectType objtype;
} ShDependObjectInfo;

static void fc_getOidListDiff(Oid *fc_list1, int *fc_nlist1, Oid *fc_list2, int *fc_nlist2);
static Oid	fc_classIdGetDbId(Oid fc_classId);
static void fc_shdepChangeDep(Relation fc_sdepRel,
						   Oid fc_classid, Oid fc_objid, int32 fc_objsubid,
						   Oid fc_refclassid, Oid fc_refobjid,
						   SharedDependencyType fc_deptype);
static void fc_shdepAddDependency(Relation fc_sdepRel,
							   Oid fc_classId, Oid fc_objectId, int32 fc_objsubId,
							   Oid fc_refclassId, Oid fc_refobjId,
							   SharedDependencyType fc_deptype);
static void fc_shdepDropDependency(Relation fc_sdepRel,
								Oid fc_classId, Oid fc_objectId, int32 fc_objsubId,
								bool fc_drop_subobjects,
								Oid fc_refclassId, Oid fc_refobjId,
								SharedDependencyType fc_deptype);
static void fc_storeObjectDescription(StringInfo fc_descs,
								   SharedDependencyObjectType fc_type,
								   ObjectAddress *fc_object,
								   SharedDependencyType fc_deptype,
								   int fc_count);


/* 
 * recordSharedDependencyOn
 *
 * 通过各自的 ObjectAddresses 记录两个对象之间的依赖关系。
 * 第一个参数是依赖对象，第二个参数是它所引用的对象（必须是共享对象）。
 *
 * 这会锁定被引用的对象，并确保它仍然存在。
 * 然后在 pg_shdepend 中创建一个条目。锁定持续到
 * 事务结束。
 *
 * 不记录对固定对象的依赖。
 */
void recordSharedDependencyOn(ObjectAddress *fc_depender,
						 ObjectAddress *fc_referenced,
						 SharedDependencyType fc_deptype)
{
	Relation	fc_sdepRel;

	/*
	 * pg_shdepend中对象不能有SubIds。
	 */
	Assert(fc_depender->objectSubId == 0);
	Assert(fc_referenced->objectSubId == 0);

	/*
	 * 在启动过程中，不执行任何操作因为pg_shdepend可能尚不存在。
	 * initdb将在启动后填充适当的pg_shdepend条目。
	 */
	if (IsBootstrapProcessingMode())
		return;

	fc_sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);

	/* 如果被引用的对象已固定，则不执行任何操作。 */
	if (!IsPinnedObject(fc_referenced->classId, fc_referenced->objectId))
	{
		fc_shdepAddDependency(fc_sdepRel, fc_depender->classId, fc_depender->objectId,
						   fc_depender->objectSubId,
						   fc_referenced->classId, fc_referenced->objectId,
						   fc_deptype);
	}

	table_close(fc_sdepRel, RowExclusiveLock);
}

/*
 * recordDependencyOnOwner
 *
 * recordSharedDependencyOn的一个方便包装器 -- 注册指定的
 * 用户为给定对象的所有者。
 *
 * 注意：确保该对象没有所有者条目是调用者的责任。
 */
void recordDependencyOnOwner(Oid fc_classId, Oid fc_objectId, Oid fc_owner)
{
	ObjectAddress fc_myself,
				fc_referenced;

	fc_myself.classId = fc_classId;
	fc_myself.objectId = fc_objectId;
	fc_myself.objectSubId = 0;

	fc_referenced.classId = AuthIdRelationId;
	fc_referenced.objectId = fc_owner;
	fc_referenced.objectSubId = 0;

	recordSharedDependencyOn(&fc_myself, &fc_referenced, SHARED_DEPENDENCY_OWNER);
}

/*
 * shdepChangeDep
 *
 * 更新共享依赖记录以考虑更新后的被引用
 * 对象。这是更改对象所有者等操作的内部工作马。
 *
 * 对于给定的依赖对象和依赖类型，不能存在超过一个的现有条目！因此在实践中，这只能用于
 * 更新SHARED_DEPENDENCY_OWNER和SHARED_DEPENDENCY_TABLESPACE
 * 条目，它们应该具备该属性。
 *
 * 如果没有先前的条目，我们假设它引用了一个固定（PINned）的
 * 对象，所以我们创建一个新条目。如果新的被引用对象是
 * 固定的，我们不创建条目（并丢弃旧的条目，如果有的话）。
 * （对于表空间，我们在某些情况下不记录依赖关系，因此
 * 还有其他可能的缺失条目的原因。）
 *
 * sdepRel必须是pg_shdepend关系，已经打开并适当
 * 上锁。
 */
static void fc_shdepChangeDep(Relation fc_sdepRel,
			   Oid fc_classid, Oid fc_objid, int32 fc_objsubid,
			   Oid fc_refclassid, Oid fc_refobjid,
			   SharedDependencyType fc_deptype)
{
	Oid			fc_dbid = fc_classIdGetDbId(fc_classid);
	HeapTuple	fc_oldtup = NULL;
	HeapTuple	fc_scantup;
	ScanKeyData fc_key[4];
	SysScanDesc fc_scan;

	/*
	 * 确保在记录依赖关系时新的被引用对象不会消失。
	 */
	shdepLockAndCheckObject(fc_refclassid, fc_refobjid);

	/*
	 * 查找先前的条目
	 */
	ScanKeyInit(&fc_key[0],
				Anum_pg_shdepend_dbid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_dbid));
	ScanKeyInit(&fc_key[1],
				Anum_pg_shdepend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_classid));
	ScanKeyInit(&fc_key[2],
				Anum_pg_shdepend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_objid));
	ScanKeyInit(&fc_key[3],
				Anum_pg_shdepend_objsubid,
				BTEqualStrategyNumber, F_INT4EQ,
				Int32GetDatum(fc_objsubid));

	fc_scan = systable_beginscan(fc_sdepRel, SharedDependDependerIndexId, true,
							  NULL, 4, fc_key);

	while ((fc_scantup = systable_getnext(fc_scan)) != NULL)
	{
		/* 如果不是目标依赖类型则忽略 */
		if (((Form_pg_shdepend) GETSTRUCT(fc_scantup))->deptype != fc_deptype)
			continue;
		/* 如果多个匹配则调用者出错 */
		if (fc_oldtup)
			elog(ERROR,
				 "multiple pg_shdepend entries for object %u/%u/%d deptype %c",
				 fc_classid, fc_objid, fc_objsubid, fc_deptype);
		fc_oldtup = heap_copytuple(fc_scantup);
	}

	systable_endscan(fc_scan);

	if (IsPinnedObject(fc_refclassid, fc_refobjid))
	{
		/* 不需要新条目，因此只需删除现有条目（如果有） */
		if (fc_oldtup)
			CatalogTupleDelete(fc_sdepRel, &fc_oldtup->t_self);
	}
	else if (fc_oldtup)
	{
		/* 需要更新现有条目 */
		Form_pg_shdepend fc_shForm = (Form_pg_shdepend) GETSTRUCT(fc_oldtup);

		/* 因为oldtup是一个副本，所以我们可以在内存中直接修改它 */
		fc_shForm->refclassid = fc_refclassid;
		fc_shForm->refobjid = fc_refobjid;

		CatalogTupleUpdate(fc_sdepRel, &fc_oldtup->t_self, fc_oldtup);
	}
	else
	{
		/* 需要插入新条目 */
		Datum		fc_values[Natts_pg_shdepend];
		bool		fc_nulls[Natts_pg_shdepend];

		memset(fc_nulls, false, sizeof(fc_nulls));

		fc_values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(fc_dbid);
		fc_values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(fc_classid);
		fc_values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(fc_objid);
		fc_values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(fc_objsubid);

		fc_values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(fc_refclassid);
		fc_values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(fc_refobjid);
		fc_values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(fc_deptype);

		/*
		 * 我们重用oldtup只是为了避免声明一个新变量，但
		 * 这无疑是一个新元组
		 */
		fc_oldtup = heap_form_tuple(RelationGetDescr(fc_sdepRel), fc_values, fc_nulls);
		CatalogTupleInsert(fc_sdepRel, fc_oldtup);
	}

	if (fc_oldtup)
		heap_freetuple(fc_oldtup);
}

/*
 * changeDependencyOnOwner
 *
 * 更新共享依赖以考虑新的所有者。
 *
 * 注意：我们不需要objsubid参数，因为只有整个对象
 * 有所有者。
 */
void changeDependencyOnOwner(Oid fc_classId, Oid fc_objectId, Oid fc_newOwnerId)
{
	Relation	fc_sdepRel;

	fc_sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);

	/* 调整SHARED_DEPENDENCY_OWNER条目 */
	fc_shdepChangeDep(fc_sdepRel,
				   fc_classId, fc_objectId, 0,
				   AuthIdRelationId, fc_newOwnerId,
				   SHARED_DEPENDENCY_OWNER);

	/*----------
	 * 对于所有者，永远不应存在SHARED_DEPENDENCY_ACL条目，
	 * 如果存在则将其删除。这可能发生在新的所有者
	 * 之前曾被授予对该对象的一些权利的情况下。
	 *
	 * 这一步类似于aclnewowner删除ACL中重复条目的操作。
	 * 我们必须这样做以处理以下场景：
	 *		A将某些权利授予对象给B
	 *		ALTER OWNER将对象的所有者更改为B
	 *		ALTER OWNER将对象的所有者更改为C
	 * 第三步将从对象的ACL中删除所有对B的提及，
	 * 但如果我们不这样做，我们仍会有B的SHARED_DEPENDENCY_ACL条目。
	 *
	 * 对于所有者不允许存在SHARED_DEPENDENCY_ACL条目的规则
	 * 使我们仅在这一处修复问题，而不必让各个ALTER OWNER例程都知道它。
	 *----------
	 */
	fc_shdepDropDependency(fc_sdepRel, fc_classId, fc_objectId, 0, true,
						AuthIdRelationId, fc_newOwnerId,
						SHARED_DEPENDENCY_ACL);

	table_close(fc_sdepRel, RowExclusiveLock);
}

/*
 * recordDependencyOnTablespace
 *
 * recordSharedDependencyOn的一个方便包装器 -- 注册指定的
 * 表空间为给定对象的默认值。
 *
 * 注意：确保该对象没有表空间条目是调用者的责任。
 */
void recordDependencyOnTablespace(Oid fc_classId, Oid fc_objectId, Oid fc_tablespace)
{
	ObjectAddress fc_myself,
				fc_referenced;

	ObjectAddressSet(fc_myself, fc_classId, fc_objectId);
	ObjectAddressSet(fc_referenced, TableSpaceRelationId, fc_tablespace);

	recordSharedDependencyOn(&fc_myself, &fc_referenced,
							 SHARED_DEPENDENCY_TABLESPACE);
}

/*
 * changeDependencyOnTablespace
 *
 * 更新共享依赖以考虑新的表空间。
 *
 * 注意：我们不需要objsubid参数，因为只有整个对象
 * 有表空间。
 */
void changeDependencyOnTablespace(Oid fc_classId, Oid fc_objectId, Oid fc_newTablespaceId)
{
	Relation	fc_sdepRel;

	fc_sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);

	if (fc_newTablespaceId != DEFAULTTABLESPACE_OID &&
		fc_newTablespaceId != InvalidOid)
		fc_shdepChangeDep(fc_sdepRel,
					   fc_classId, fc_objectId, 0,
					   TableSpaceRelationId, fc_newTablespaceId,
					   SHARED_DEPENDENCY_TABLESPACE);
	else
		fc_shdepDropDependency(fc_sdepRel,
							fc_classId, fc_objectId, 0, true,
							InvalidOid, InvalidOid,
							SHARED_DEPENDENCY_INVALID);

	table_close(fc_sdepRel, RowExclusiveLock);
}

/*
 * getOidListDiff
 *		updateAclDependencies的帮助函数。
 *
 * 需要两个Oid数组并移除两个数组中共有的元素，
 * 仅保留那些在一个输入中但不在另一个中的元素。
 * 我们假设两个数组都已排序且去重。
 */
static void fc_getOidListDiff(Oid *fc_list1, int *fc_nlist1, Oid *fc_list2, int *fc_nlist2)
{
	int			fc_in1,
				fc_in2,
				fc_out1,
				fc_out2;

	fc_in1 = fc_in2 = fc_out1 = fc_out2 = 0;
	while (fc_in1 < *fc_nlist1 && fc_in2 < *fc_nlist2)
	{
		if (fc_list1[fc_in1] == fc_list2[fc_in2])
		{
			/* 跳过重复项 */
			fc_in1++;
			fc_in2++;
		}
		else if (fc_list1[fc_in1] < fc_list2[fc_in2])
		{
			/* list1[in1]不在list2中 */
			fc_list1[fc_out1++] = fc_list1[fc_in1++];
		}
		else
		{
			/* list2[in2]不在list1中 */
			fc_list2[fc_out2++] = fc_list2[fc_in2++];
		}
	}

	/* 任何剩余的list1条目不在list2中 */
	while (fc_in1 < *fc_nlist1)
	{
		fc_list1[fc_out1++] = fc_list1[fc_in1++];
	}

	/* 任何剩余的 list2 条目不在 list1 中 */
	while (fc_in2 < *fc_nlist2)
	{
		fc_list2[fc_out2++] = fc_list2[fc_in2++];
	}

	*fc_nlist1 = fc_out1;
	*fc_nlist2 = fc_out2;
}

/*
 * updateAclDependencies
 *		在 GRANT/REVOKE 期间更新对象 ACL 的 pg_shdepend 信息。
 *
 * classId, objectId, objsubId：标识此 ACL 的对象
 * ownerId：拥有对象的角色
 * noldmembers, oldmembers：出现在旧 ACL 中的 roleids 数组
 * nnewmembers, newmembers：出现在新 ACL 中的 roleids 数组
 *
 * 我们计算新旧角色列表之间的差异，
 * 然后根据需要在 pg_shdepend 中插入或删除。
 *
 * 请注意，我们不能在 GRANT 时盲目插入所有引用的角色，
 * 因为这样会导致重复注册的依赖项。我们可以在插入前检查元组的存在性，
 * 但这似乎比我们在这里所做的更昂贵。同样，在 REVOKE 期间我们也不能盲目删除，
 * 因为用户可能仍然拥有其他特权。REVOKE 也可能
 * 实际上添加依赖项，因为以前隐式默认 ACL 的实例化（尽管目前所有此类依赖项应该
 * 仅针对拥有角色，我们在此忽略）。
 *
 * 注意：两个输入数组必须已经排序并去重。 （通常它们是通过 aclmembers()
 * 从 ACL 数组中提取的，该函数处理这两个要求。）
 * 在返回之前会释放数组。
 */
void updateAclDependencies(Oid fc_classId, Oid fc_objectId, int32 fc_objsubId,
					  Oid fc_ownerId,
					  int fc_noldmembers, Oid *fc_oldmembers,
					  int fc_nnewmembers, Oid *fc_newmembers)
{
	Relation	fc_sdepRel;
	int			fc_i;

	/*
	 * 移除在两个列表中都存在的条目；这些代表我们不需要更改的现有依赖项。
	 *
	 * 可以覆盖输入，因为我们会释放它们。
	 */
	fc_getOidListDiff(fc_oldmembers, &fc_noldmembers, fc_newmembers, &fc_nnewmembers);

	if (fc_noldmembers > 0 || fc_nnewmembers > 0)
	{
		fc_sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);

		/* 添加不存在的新依赖项 */
		for (fc_i = 0; fc_i < fc_nnewmembers; fc_i++)
		{
			Oid			fc_roleid = fc_newmembers[fc_i];

			/*
			 * 跳过所有者：他有一个 OWNER shdep 条目。 (这不仅仅是空间优化；
			 * 它使 ALTER OWNER 更加容易。有关 changeDependencyOnOwner 中的
			 * 注释，请参见相关说明。)
			 */
			if (fc_roleid == fc_ownerId)
				continue;

			/* 跳过已固定角色；它们不需要依赖项条目 */
			if (IsPinnedObject(AuthIdRelationId, fc_roleid))
				continue;

			fc_shdepAddDependency(fc_sdepRel, fc_classId, fc_objectId, fc_objsubId,
							   AuthIdRelationId, fc_roleid,
							   SHARED_DEPENDENCY_ACL);
		}

		/* 删除不再使用的旧依赖项 */
		for (fc_i = 0; fc_i < fc_noldmembers; fc_i++)
		{
			Oid			fc_roleid = fc_oldmembers[fc_i];

			/* 跳过所有者，与上述相同 */
			if (fc_roleid == fc_ownerId)
				continue;

			/* 跳过已固定角色 */
			if (IsPinnedObject(AuthIdRelationId, fc_roleid))
				continue;

			fc_shdepDropDependency(fc_sdepRel, fc_classId, fc_objectId, fc_objsubId,
								false,	/* objsubId 的精确匹配 */
								AuthIdRelationId, fc_roleid,
								SHARED_DEPENDENCY_ACL);
		}

		table_close(fc_sdepRel, RowExclusiveLock);
	}

	if (fc_oldmembers)
		pfree(fc_oldmembers);
	if (fc_newmembers)
		pfree(fc_newmembers);
}

/*
 * 一个结构体，用于跟踪在其他数据库中发现的依赖项。
 */
typedef struct
{
	Oid			dbOid;
	int			count;
} remoteDep;

/*
 * ShDependObjectInfo 项的 qsort 比较器
 */
static int fc_shared_dependency_comparator(const void *fc_a, const void *fc_b)
{
	const ShDependObjectInfo *fc_obja = (const ShDependObjectInfo *) fc_a;
	const ShDependObjectInfo *fc_objb = (const ShDependObjectInfo *) fc_b;

	/*
	 * 主排序键是 OID 升序。
	 */
	if (fc_obja->object.objectId < fc_objb->object.objectId)
		return -1;
	if (fc_obja->object.objectId > fc_objb->object.objectId)
		return 1;

	/*
	 * 接下来按目录 ID 排序，以防相同的 OID 出现在不同
	 * 的目录中。这里的排序方向相当任意。
	 */
	if (fc_obja->object.classId < fc_objb->object.classId)
		return -1;
	if (fc_obja->object.classId > fc_objb->object.classId)
		return 1;

	/*
	 * 按对象的 subId 排序。
	 *
	 * 我们将 subId 作为无符号整数进行排序，这样 0（整个对象）就会
	 * 排在首位。
	 */
	if ((unsigned int) fc_obja->object.objectSubId < (unsigned int) fc_objb->object.objectSubId)
		return -1;
	if ((unsigned int) fc_obja->object.objectSubId > (unsigned int) fc_objb->object.objectSubId)
		return 1;

	/*
	 * 最后，按deptype排序，以防同一对象具有多个依赖类型。
	 * （请注意，objtype 的排序不需要考虑，因其由目录 OID 确定。）
	 */
	if (fc_obja->deptype < fc_objb->deptype)
		return -1;
	if (fc_obja->deptype > fc_objb->deptype)
		return 1;

	return 0;
}

/*
 * checkSharedDependencies
 *
 * 检查是否存在给定共享对象的共享依赖项；如果存在，返回真。
 *
 * 此外，还返回一个包含依赖于共享对象的对象描述的以换行符分隔的字符串，
 * 如果没有找到则返回 NULL。我们实际上返回两个这样的字符串；
 * “detail”结果适合返回给客户端作为 errdetail() 字符串，并且大小有限。
 * “detail_log”字符串可能更长，仅应发送到服务器日志。
 *
 * 我们可以找到三种不同类型的依赖关系：对当前数据库对象的依赖；
 * 对共享对象的依赖；以及对其他数据库本地对象的依赖。我们可以（并且确实
 * 行）提供前两种对象的描述，但无法为“远程”对象提供描述，因此只提供数量。
 */
bool checkSharedDependencies(Oid fc_classId, Oid fc_objectId,
						char **fc_detail_msg, char **fc_detail_log_msg)
{
	Relation	fc_sdepRel;
	ScanKeyData fc_key[2];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;
	int			fc_numReportedDeps = 0;
	int			fc_numNotReportedDeps = 0;
	int			fc_numNotReportedDbs = 0;
	List	   *fc_remDeps = NIL;
	ListCell   *fc_cell;
	ObjectAddress fc_object;
	ShDependObjectInfo *fc_objects;
	int			fc_numobjects;
	int			fc_allocedobjects;
	StringInfoData fc_descs;
	StringInfoData fc_alldescs;

	/* 此情况可以迅速处理 */
	if (IsPinnedObject(fc_classId, fc_objectId))
	{
		fc_object.classId = fc_classId;
		fc_object.objectId = fc_objectId;
		fc_object.objectSubId = 0;
		ereport(ERROR,
				(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
				 errmsg("cannot drop %s because it is required by the database system",
						getObjectDescription(&fc_object, false))));
	}

	/*
	 * 我们将报告给客户端的依赖项数量限制为 MAX_REPORTED_DEPS，
	 * 因为客户端软件可能无法很好地处理庞大的错误字符串。服务器日志
	 * 始终会获得完整报告。
	 *
	 * 为了保证回归测试结果的稳定性，我们在报告前按 OID 对本地和共享
	 * 对象进行排序。不过，我们不关心其他数据库的报告顺序。
	 */
#define MAX_REPORTED_DEPS 100

	fc_allocedobjects = 128;		/* 任意初始数组大小 */
	fc_objects = (ShDependObjectInfo *)
		palloc(fc_allocedobjects * sizeof(ShDependObjectInfo));
	fc_numobjects = 0;
	initStringInfo(&fc_descs);
	initStringInfo(&fc_alldescs);

	fc_sdepRel = table_open(SharedDependRelationId, AccessShareLock);

	ScanKeyInit(&fc_key[0],
				Anum_pg_shdepend_refclassid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_classId));
	ScanKeyInit(&fc_key[1],
				Anum_pg_shdepend_refobjid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_objectId));

	fc_scan = systable_beginscan(fc_sdepRel, SharedDependReferenceIndexId, true,
							  NULL, 2, fc_key);

	while (HeapTupleIsValid(fc_tup = systable_getnext(fc_scan)))
	{
		Form_pg_shdepend fc_sdepForm = (Form_pg_shdepend) GETSTRUCT(fc_tup);

		fc_object.classId = fc_sdepForm->classid;
		fc_object.objectId = fc_sdepForm->objid;
		fc_object.objectSubId = fc_sdepForm->objsubid;

		/*
		 * 如果它是本数据库的本地依赖项或共享对象，则将其添加到对象数组中。
		 *
		 * 如果它是远程依赖项，请追踪它，以便我们可以稍后报告它们的数量。
		 */
		if (fc_sdepForm->dbid == MyDatabaseId ||
			fc_sdepForm->dbid == InvalidOid)
		{
			if (fc_numobjects >= fc_allocedobjects)
			{
				fc_allocedobjects *= 2;
				fc_objects = (ShDependObjectInfo *)
					repalloc(fc_objects,
							 fc_allocedobjects * sizeof(ShDependObjectInfo));
			}
			fc_objects[fc_numobjects].object = fc_object;
			fc_objects[fc_numobjects].deptype = fc_sdepForm->deptype;
			fc_objects[fc_numobjects].objtype = (fc_sdepForm->dbid == MyDatabaseId) ?
				LOCAL_OBJECT : SHARED_OBJECT;
			fc_numobjects++;
		}
		else
		{
			/* 它既不是本地的也不是共享的，因此它必须是远程的。 */
			remoteDep  *fc_dep;
			bool		fc_stored = false;

			/*
			 * XXX 此信息保存在一个简单的列表中。也许这对性能不好，
			 * 但使用哈希表似乎复杂且没必要。预期的数据库数量反正不会高，
			 * 我想。
			 */
			foreach(fc_cell, fc_remDeps)
			{
				fc_dep = lfirst(fc_cell);
				if (fc_dep->dbOid == fc_sdepForm->dbid)
				{
					fc_dep->count++;
					fc_stored = true;
					break;
				}
			}
			if (!fc_stored)
			{
				fc_dep = (remoteDep *) palloc(sizeof(remoteDep));
				fc_dep->dbOid = fc_sdepForm->dbid;
				fc_dep->count = 1;
				fc_remDeps = lappend(fc_remDeps, fc_dep);
			}
		}
	}

	systable_endscan(fc_scan);

	table_close(fc_sdepRel, AccessShareLock);

	/*
	 * 排序并报告本地和共享对象。
	 */
	if (fc_numobjects > 1)
		qsort((void *) fc_objects, fc_numobjects,
			  sizeof(ShDependObjectInfo), fc_shared_dependency_comparator);

	for (int fc_i = 0; fc_i < fc_numobjects; fc_i++)
	{
		if (fc_numReportedDeps < MAX_REPORTED_DEPS)
		{
			fc_numReportedDeps++;
			fc_storeObjectDescription(&fc_descs,
								   fc_objects[fc_i].objtype,
								   &fc_objects[fc_i].object,
								   fc_objects[fc_i].deptype,
								   0);
		}
		else
			fc_numNotReportedDeps++;
		fc_storeObjectDescription(&fc_alldescs,
							   fc_objects[fc_i].objtype,
							   &fc_objects[fc_i].object,
							   fc_objects[fc_i].deptype,
							   0);
	}

	/*
	 * 总结远程数据库中的依赖关系。
	 */
	foreach(fc_cell, fc_remDeps)
	{
		remoteDep  *fc_dep = lfirst(fc_cell);

		fc_object.classId = DatabaseRelationId;
		fc_object.objectId = fc_dep->dbOid;
		fc_object.objectSubId = 0;

		if (fc_numReportedDeps < MAX_REPORTED_DEPS)
		{
			fc_numReportedDeps++;
			fc_storeObjectDescription(&fc_descs, REMOTE_OBJECT, &fc_object,
								   SHARED_DEPENDENCY_INVALID, fc_dep->count);
		}
		else
			fc_numNotReportedDbs++;
		fc_storeObjectDescription(&fc_alldescs, REMOTE_OBJECT, &fc_object,
							   SHARED_DEPENDENCY_INVALID, fc_dep->count);
	}

	pfree(fc_objects);
	list_free_deep(fc_remDeps);

	if (fc_descs.len == 0)
	{
		pfree(fc_descs.data);
		pfree(fc_alldescs.data);
		*fc_detail_msg = *fc_detail_log_msg = NULL;
		return false;
	}

	if (fc_numNotReportedDeps > 0)
		appendStringInfo(&fc_descs, ngettext("\nand %d other object "
										  "(see server log for list)",
										  "\nand %d other objects "
										  "(see server log for list)",
										  fc_numNotReportedDeps),
						 fc_numNotReportedDeps);
	if (fc_numNotReportedDbs > 0)
		appendStringInfo(&fc_descs, ngettext("\nand objects in %d other database "
										  "(see server log for list)",
										  "\nand objects in %d other databases "
										  "(see server log for list)",
										  fc_numNotReportedDbs),
						 fc_numNotReportedDbs);

	*fc_detail_msg = fc_descs.data;
	*fc_detail_log_msg = fc_alldescs.data;
	return true;
}


/*
 * copyTemplateDependencies
 *
 * 创建新数据库的初始共享依赖关系的例程。
 * 我们只是简单地从模板数据库复制依赖项。
 */
void copyTemplateDependencies(Oid fc_templateDbId, Oid fc_newDbId)
{
	Relation	fc_sdepRel;
	TupleDesc	fc_sdepDesc;
	ScanKeyData fc_key[1];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;
	CatalogIndexState fc_indstate;
	TupleTableSlot **fc_slot;
	int			fc_max_slots,
				fc_slot_init_count,
				fc_slot_stored_count;

	fc_sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);
	fc_sdepDesc = RelationGetDescr(fc_sdepRel);

	/*
	 * 分配要使用的槽，但在知道它们将被使用之前，延迟高成本的初始化。
	 */
	fc_max_slots = MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_shdepend);
	fc_slot = palloc(sizeof(TupleTableSlot *) * fc_max_slots);

	fc_indstate = CatalogOpenIndexes(fc_sdepRel);

	/* 扫描所有 dbid = templateDbId 的条目 */
	ScanKeyInit(&fc_key[0],
				Anum_pg_shdepend_dbid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_templateDbId));

	fc_scan = systable_beginscan(fc_sdepRel, SharedDependDependerIndexId, true,
							  NULL, 1, fc_key);

	/* 当前存储元组的槽数 */
	fc_slot_stored_count = 0;
	/* 当前初始化的槽数 */
	fc_slot_init_count = 0;

	/*
	 * 复制原始数据库的条目，将数据库 Id 更改为新数据库的 Id。
	 * 请注意，因为我们不复制 dbId == 0 的行（即，描述依赖共享对象的行），
	 * 所以我们不会复制模板数据库本身的所有权依赖项；这正是我们想要的。
	 */
	while (HeapTupleIsValid(fc_tup = systable_getnext(fc_scan)))
	{
		Form_pg_shdepend fc_shdep;

		if (fc_slot_init_count < fc_max_slots)
		{
			fc_slot[fc_slot_stored_count] = MakeSingleTupleTableSlot(fc_sdepDesc, &TTSOpsHeapTuple);
			fc_slot_init_count++;
		}

		ExecClearTuple(fc_slot[fc_slot_stored_count]);

		memset(fc_slot[fc_slot_stored_count]->tts_isnull, false,
			   fc_slot[fc_slot_stored_count]->tts_tupleDescriptor->natts * sizeof(bool));

		fc_shdep = (Form_pg_shdepend) GETSTRUCT(fc_tup);

		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(fc_newDbId);
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_shdepend_classid - 1] = fc_shdep->classid;
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_shdepend_objid - 1] = fc_shdep->objid;
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_shdepend_objsubid - 1] = fc_shdep->objsubid;
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_shdepend_refclassid - 1] = fc_shdep->refclassid;
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_shdepend_refobjid - 1] = fc_shdep->refobjid;
		fc_slot[fc_slot_stored_count]->tts_values[Anum_pg_shdepend_deptype - 1] = fc_shdep->deptype;

		ExecStoreVirtualTuple(fc_slot[fc_slot_stored_count]);
		fc_slot_stored_count++;

		/* 如果槽已满，插入一批元组 */
		if (fc_slot_stored_count == fc_max_slots)
		{
			CatalogTuplesMultiInsertWithInfo(fc_sdepRel, fc_slot, fc_slot_stored_count, fc_indstate);
			fc_slot_stored_count = 0;
		}
	}

	/* 插入缓冲区中剩余的任何元组 */
	if (fc_slot_stored_count > 0)
		CatalogTuplesMultiInsertWithInfo(fc_sdepRel, fc_slot, fc_slot_stored_count, fc_indstate);

	systable_endscan(fc_scan);

	CatalogCloseIndexes(fc_indstate);
	table_close(fc_sdepRel, RowExclusiveLock);

	/* 仅删除使用的槽数 */
	for (int fc_i = 0; fc_i < fc_slot_init_count; fc_i++)
		ExecDropSingleTupleTableSlot(fc_slot[fc_i]);
	pfree(fc_slot);
}

/*
 * dropDatabaseDependencies
 *
 * 删除与正在删除的数据库相关的 pg_shdepend 条目。
 */
void dropDatabaseDependencies(Oid fc_databaseId)
{
	Relation	fc_sdepRel;
	ScanKeyData fc_key[1];
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;

	fc_sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);

	/*
	 * 首先，删除 dbid 字段中具有数据库 Oid 的所有条目。
	 */
	ScanKeyInit(&fc_key[0],
				Anum_pg_shdepend_dbid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_databaseId));
	/* 我们不指定其他索引字段 */

	fc_scan = systable_beginscan(fc_sdepRel, SharedDependDependerIndexId, true,
							  NULL, 1, fc_key);

	while (HeapTupleIsValid(fc_tup = systable_getnext(fc_scan)))
	{
		CatalogTupleDelete(fc_sdepRel, &fc_tup->t_self);
	}

	systable_endscan(fc_scan);

	/* 现在删除与数据库本身相关的所有条目 */
	fc_shdepDropDependency(fc_sdepRel, DatabaseRelationId, fc_databaseId, 0, true,
						InvalidOid, InvalidOid,
						SHARED_DEPENDENCY_INVALID);

	table_close(fc_sdepRel, RowExclusiveLock);
}

/*
 * deleteSharedDependencyRecordsFor
 *
 * 删除与正在删除或修改的对象相对应的所有 pg_shdepend 条目。
 * 假定该对象为共享对象或当前数据库本地对象（classId 告诉我们哪种）。
 *
 * 如果 objectSubId 为零，则我们正在删除整个对象，因此也要删除
 * 子对象的 pg_shdepend 条目。
 */
void deleteSharedDependencyRecordsFor(Oid fc_classId, Oid fc_objectId, int32 fc_objectSubId)
{
	Relation	fc_sdepRel;

	fc_sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);

	fc_shdepDropDependency(fc_sdepRel, fc_classId, fc_objectId, fc_objectSubId,
						(fc_objectSubId == 0),
						InvalidOid, InvalidOid,
						SHARED_DEPENDENCY_INVALID);

	table_close(fc_sdepRel, RowExclusiveLock);
}

/*
 * shdepAddDependency
 *		插入到 pg_shdepend 的内部工作马
 *
 * sdepRel 必须是 pg_shdepend 关系，已经打开并适当锁定。
 */
static void fc_shdepAddDependency(Relation fc_sdepRel,
				   Oid fc_classId, Oid fc_objectId, int32 fc_objsubId,
				   Oid fc_refclassId, Oid fc_refobjId,
				   SharedDependencyType fc_deptype)
{
	HeapTuple	fc_tup;
	Datum		fc_values[Natts_pg_shdepend];
	bool		fc_nulls[Natts_pg_shdepend];

	/*
	 * 确保在我们记录对它的依赖关系时对象不会消失。
	 * DROP 例程在检查共享依赖关系之前应独占锁定对象。
	 */
	shdepLockAndCheckObject(fc_refclassId, fc_refobjId);

	memset(fc_nulls, false, sizeof(fc_nulls));

	/*
	 * 形成新的元组并记录依赖关系。
	 */
	fc_values[Anum_pg_shdepend_dbid - 1] = ObjectIdGetDatum(fc_classIdGetDbId(fc_classId));
	fc_values[Anum_pg_shdepend_classid - 1] = ObjectIdGetDatum(fc_classId);
	fc_values[Anum_pg_shdepend_objid - 1] = ObjectIdGetDatum(fc_objectId);
	fc_values[Anum_pg_shdepend_objsubid - 1] = Int32GetDatum(fc_objsubId);

	fc_values[Anum_pg_shdepend_refclassid - 1] = ObjectIdGetDatum(fc_refclassId);
	fc_values[Anum_pg_shdepend_refobjid - 1] = ObjectIdGetDatum(fc_refobjId);
	fc_values[Anum_pg_shdepend_deptype - 1] = CharGetDatum(fc_deptype);

	fc_tup = heap_form_tuple(fc_sdepRel->rd_att, fc_values, fc_nulls);

	CatalogTupleInsert(fc_sdepRel, fc_tup);

	/* 清理 */
	heap_freetuple(fc_tup);
}

/*
 * shdepDropDependency
 *		删除 pg_shdepend 中条目的内部工作马。
 *
 * 我们删除具有以下属性的条目：
 *		依赖对象是由 classId/objectId/objsubId 确定的对象
 *		如果 refclassId 不是 InvalidOid，它必须与条目的 refclassid 匹配
 *		如果 refobjId 不是 InvalidOid，它必须与条目的 refobjid 匹配
 *		如果 deptype 不是 SHARED_DEPENDENCY_INVALID，它必须与条目的 deptype 匹配
 *
 * 如果 drop_subobjects 为真，我们忽略 objsubId，并考虑所有
 * 匹配 classId/objectId 的条目。
 *
 * sdepRel 必须是 pg_shdepend 关系，已经打开并适当地
 * 锁定。
 */
static void fc_shdepDropDependency(Relation fc_sdepRel,
					Oid fc_classId, Oid fc_objectId, int32 fc_objsubId,
					bool fc_drop_subobjects,
					Oid fc_refclassId, Oid fc_refobjId,
					SharedDependencyType fc_deptype)
{
	ScanKeyData fc_key[4];
	int			fc_nkeys;
	SysScanDesc fc_scan;
	HeapTuple	fc_tup;

	/* 扫描匹配依赖对象的条目 */
	ScanKeyInit(&fc_key[0],
				Anum_pg_shdepend_dbid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_classIdGetDbId(fc_classId)));
	ScanKeyInit(&fc_key[1],
				Anum_pg_shdepend_classid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_classId));
	ScanKeyInit(&fc_key[2],
				Anum_pg_shdepend_objid,
				BTEqualStrategyNumber, F_OIDEQ,
				ObjectIdGetDatum(fc_objectId));
	if (fc_drop_subobjects)
		fc_nkeys = 3;
	else
	{
		ScanKeyInit(&fc_key[3],
					Anum_pg_shdepend_objsubid,
					BTEqualStrategyNumber, F_INT4EQ,
					Int32GetDatum(fc_objsubId));
		fc_nkeys = 4;
	}

	fc_scan = systable_beginscan(fc_sdepRel, SharedDependDependerIndexId, true,
							  NULL, fc_nkeys, fc_key);

	while (HeapTupleIsValid(fc_tup = systable_getnext(fc_scan)))
	{
		Form_pg_shdepend fc_shdepForm = (Form_pg_shdepend) GETSTRUCT(fc_tup);

		/* 根据附加参数过滤条目 */
		if (OidIsValid(fc_refclassId) && fc_shdepForm->refclassid != fc_refclassId)
			continue;
		if (OidIsValid(fc_refobjId) && fc_shdepForm->refobjid != fc_refobjId)
			continue;
		if (fc_deptype != SHARED_DEPENDENCY_INVALID &&
			fc_shdepForm->deptype != fc_deptype)
			continue;

		/* 好的，删除它 */
		CatalogTupleDelete(fc_sdepRel, &fc_tup->t_self);
	}

	systable_endscan(fc_scan);
}

/*
 * classIdGetDbId
 *
 * 根据包含对象的目录的 OID 获取应该在 pg_shdepend 中使用的数据库 Id。对于共享对象，它是 0
 * (InvalidOid)；对于所有其他对象，它是当前数据库 Id。
 */
static Oid fc_classIdGetDbId(Oid fc_classId)
{
	Oid			fc_dbId;

	if (IsSharedRelation(fc_classId))
		fc_dbId = InvalidOid;
	else
		fc_dbId = MyDatabaseId;

	return fc_dbId;
}

/*
 * shdepLockAndCheckObject
 *
 * 锁定我们即将记录依赖关系的对象。
 * 在锁定后，验证在我们不注意的时候它没有被删除。如果对象已经被删除，此函数将
 * 不返回！
 */
void shdepLockAndCheckObject(Oid fc_classId, Oid fc_objectId)
{
	/* AccessShareLock 应该可以，因为我们不修改对象 */
	LockSharedObject(fc_classId, fc_objectId, 0, AccessShareLock);

	switch (fc_classId)
	{
		case AuthIdRelationId:
			if (!SearchSysCacheExists1(AUTHOID, ObjectIdGetDatum(fc_objectId)))
				ereport(ERROR,
						(errcode(ERRCODE_UNDEFINED_OBJECT),
						 errmsg("role %u was concurrently dropped",
								fc_objectId)));
			break;

		case TableSpaceRelationId:
			{
				/* 由于没有 pg_tablespace 的 syscache，执行以下操作： */
				char	   *fc_tablespace = get_tablespace_name(fc_objectId);

				if (fc_tablespace == NULL)
					ereport(ERROR,
							(errcode(ERRCODE_UNDEFINED_OBJECT),
							 errmsg("tablespace %u was concurrently dropped",
									fc_objectId)));
				pfree(fc_tablespace);
				break;
			}

		case DatabaseRelationId:
			{
				/* 由于没有 pg_database 的 syscache，执行以下操作： */
				char	   *fc_database = get_database_name(fc_objectId);

				if (fc_database == NULL)
					ereport(ERROR,
							(errcode(ERRCODE_UNDEFINED_OBJECT),
							 errmsg("database %u was concurrently dropped",
									fc_objectId)));
				pfree(fc_database);
				break;
			}


		default:
			elog(ERROR, "unrecognized shared classId: %u", fc_classId);
	}
}


/*
 * storeObjectDescription
 *		将依赖对象的描述附加到 "descs"
 *
 * 在搜索共享对象的依赖关系时，我们将找到的依赖对象的描述保存在一个字符串中，稍后
 * 当有人尝试删除引用的共享对象时将其传递给 ereport() 的 DETAIL 字段。
 *
 * 当类型为 LOCAL_OBJECT 或 SHARED_OBJECT 时，我们期望对象是
 * 依赖对象，deptype 是依赖类型，count 在此情况下未使用。
 * 当类型为 REMOTE_OBJECT 时，我们期望对象是数据库对象，
 * 并且 count 非零；在这种情况下不使用 deptype。
 */
static void fc_storeObjectDescription(StringInfo fc_descs,
					   SharedDependencyObjectType fc_type,
					   ObjectAddress *fc_object,
					   SharedDependencyType fc_deptype,
					   int fc_count)
{
	char	   *fc_objdesc = getObjectDescription(fc_object, false);

	/*
	 * 并发删除的对象不需要报告。
	 */
	if (fc_objdesc == NULL)
		return;

	/* 用换行符分隔条目 */
	if (fc_descs->len != 0)
		appendStringInfoChar(fc_descs, '\n');

	switch (fc_type)
	{
		case LOCAL_OBJECT:
		case SHARED_OBJECT:
			if (fc_deptype == SHARED_DEPENDENCY_OWNER)
				appendStringInfo(fc_descs, _("owner of %s"), fc_objdesc);
			else if (fc_deptype == SHARED_DEPENDENCY_ACL)
				appendStringInfo(fc_descs, _("privileges for %s"), fc_objdesc);
			else if (fc_deptype == SHARED_DEPENDENCY_POLICY)
				appendStringInfo(fc_descs, _("target of %s"), fc_objdesc);
			else if (fc_deptype == SHARED_DEPENDENCY_TABLESPACE)
				appendStringInfo(fc_descs, _("tablespace for %s"), fc_objdesc);
			else
				elog(ERROR, "unrecognized dependency type: %d",
					 (int) fc_deptype);
			break;

		case REMOTE_OBJECT:
			/* 翻译者：%s 将始终是 "数据库 %s" */
			appendStringInfo(fc_descs, ngettext("%d object in %s",
											 "%d objects in %s",
											 fc_count),
							 fc_count, fc_objdesc);
			break;

		default:
			elog(ERROR, "unrecognized object type: %d", fc_type);
	}

	pfree(fc_objdesc);
}


/*
 * shdepDropOwned
 *
 * 删除由任何一个给定 RoleIds 拥有的对象。如果角色有
 * 权限访问对象，授予也将被删除（但对象
 * 当然不会）。
 *
 * 我们可以在扫描时立即撤销授权，但删除操作被
 * 保存并通过 performMultipleDeletions 一次性完成。这
 * 是必要的，以便我们不会因为尝试以错误的顺序删除
 * 互相依赖的对象而导致失败。
 */
void shdepDropOwned(List *fc_roleids, DropBehavior fc_behavior)
{
	Relation	fc_sdepRel;
	ListCell   *fc_cell;
	ObjectAddresses *fc_deleteobjs;

	fc_deleteobjs = new_object_addresses();

	/*
	 * 我们不需要这样强的锁，但我们会调用获取
	 * RowExclusiveLock 的例程。为了避免潜在的
	 * 死锁故障，最好现在就获取它。
	 */
	fc_sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);

	/*
	 * 对于每个角色，查找依赖对象并使用
	 * 常规（非共享）依赖管理删除它们。
	 */
	foreach(fc_cell, fc_roleids)
	{
		Oid			fc_roleid = lfirst_oid(fc_cell);
		ScanKeyData fc_key[2];
		SysScanDesc fc_scan;
		HeapTuple	fc_tuple;

		/* 不适用于固定对象 */
		if (IsPinnedObject(AuthIdRelationId, fc_roleid))
		{
			ObjectAddress fc_obj;

			fc_obj.classId = AuthIdRelationId;
			fc_obj.objectId = fc_roleid;
			fc_obj.objectSubId = 0;

			ereport(ERROR,
					(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
					 errmsg("cannot drop objects owned by %s because they are "
							"required by the database system",
							getObjectDescription(&fc_obj, false))));
		}

		ScanKeyInit(&fc_key[0],
					Anum_pg_shdepend_refclassid,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(AuthIdRelationId));
		ScanKeyInit(&fc_key[1],
					Anum_pg_shdepend_refobjid,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(fc_roleid));

		fc_scan = systable_beginscan(fc_sdepRel, SharedDependReferenceIndexId, true,
								  NULL, 2, fc_key);

		while ((fc_tuple = systable_getnext(fc_scan)) != NULL)
		{
			Form_pg_shdepend fc_sdepForm = (Form_pg_shdepend) GETSTRUCT(fc_tuple);
			ObjectAddress fc_obj;

			/*
			 * 我们仅在共享对象和当前数据库中的对象
			 * 上操作
			 */
			if (fc_sdepForm->dbid != MyDatabaseId &&
				fc_sdepForm->dbid != InvalidOid)
				continue;

			switch (fc_sdepForm->deptype)
			{
					/* 不应发生 */
				case SHARED_DEPENDENCY_INVALID:
					elog(ERROR, "unexpected dependency type");
					break;
				case SHARED_DEPENDENCY_ACL:
					RemoveRoleFromObjectACL(fc_roleid,
											fc_sdepForm->classid,
											fc_sdepForm->objid);
					break;
				case SHARED_DEPENDENCY_POLICY:

					/*
					 * 尝试从策略中删除角色；如果无法做到，则删除
					 * 策略。
					 */
					if (!RemoveRoleFromObjectPolicy(fc_roleid,
													fc_sdepForm->classid,
													fc_sdepForm->objid))
					{
						fc_obj.classId = fc_sdepForm->classid;
						fc_obj.objectId = fc_sdepForm->objid;
						fc_obj.objectSubId = fc_sdepForm->objsubid;

						/*
						 * 获取对象的锁，然后验证此依赖关系
						 * 仍然相关。如果没有，可能是对象已经
						 * 被删除或策略已被修改。在这种情况下忽略
						 * 该对象。
						 */
						AcquireDeletionLock(&fc_obj, 0);
						if (!systable_recheck_tuple(fc_scan, fc_tuple))
						{
							ReleaseDeletionLock(&fc_obj);
							break;
						}
						add_exact_object_address(&fc_obj, fc_deleteobjs);
					}
					break;
				case SHARED_DEPENDENCY_OWNER:
					/* 如果是本地对象，保存以便稍后删除 */
					if (fc_sdepForm->dbid == MyDatabaseId)
					{
						fc_obj.classId = fc_sdepForm->classid;
						fc_obj.objectId = fc_sdepForm->objid;
						fc_obj.objectSubId = fc_sdepForm->objsubid;
						/* as above */
						AcquireDeletionLock(&fc_obj, 0);
						if (!systable_recheck_tuple(fc_scan, fc_tuple))
						{
							ReleaseDeletionLock(&fc_obj);
							break;
						}
						add_exact_object_address(&fc_obj, fc_deleteobjs);
					}
					break;
			}
		}

		systable_endscan(fc_scan);
	}

	/*
	 * 为了删除报告顺序的稳定性，在删除之前将对象排序为
	 * 大致的逆创建顺序（这也可能稍微加快删除的速度，
	 * 因为减少了因依赖关系而重新排列对象的机会）。
	 */
	sort_object_addresses(fc_deleteobjs);

	/* 依赖机制实际完成工作 */
	performMultipleDeletions(fc_deleteobjs, fc_behavior, 0);

	table_close(fc_sdepRel, RowExclusiveLock);

	free_object_addresses(fc_deleteobjs);
}

/*
 * shdepReassignOwned
 *
 * 将任何角色在 roleids 中拥有的对象的所有者更改为
 * newrole。授权不会被触碰。
 */
void shdepReassignOwned(List *fc_roleids, Oid fc_newrole)
{
	Relation	fc_sdepRel;
	ListCell   *fc_cell;

	/*
	 * 我们不需要这样强的锁，但我们会调用获取
	 * RowExclusiveLock 的例程。为了避免潜在的
	 * 死锁问题，最好现在就获取它。
	 */
	fc_sdepRel = table_open(SharedDependRelationId, RowExclusiveLock);

	foreach(fc_cell, fc_roleids)
	{
		SysScanDesc fc_scan;
		ScanKeyData fc_key[2];
		HeapTuple	fc_tuple;
		Oid			fc_roleid = lfirst_oid(fc_cell);

		/* 拒绝处理固定角色 */
		if (IsPinnedObject(AuthIdRelationId, fc_roleid))
		{
			ObjectAddress fc_obj;

			fc_obj.classId = AuthIdRelationId;
			fc_obj.objectId = fc_roleid;
			fc_obj.objectSubId = 0;

			ereport(ERROR,
					(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
					 errmsg("cannot reassign ownership of objects owned by %s because they are required by the database system",
							getObjectDescription(&fc_obj, false))));

			/*
			 * 没必要说实话，就是我们根本没有追踪这些依赖关系...
			 */
		}

		ScanKeyInit(&fc_key[0],
					Anum_pg_shdepend_refclassid,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(AuthIdRelationId));
		ScanKeyInit(&fc_key[1],
					Anum_pg_shdepend_refobjid,
					BTEqualStrategyNumber, F_OIDEQ,
					ObjectIdGetDatum(fc_roleid));

		fc_scan = systable_beginscan(fc_sdepRel, SharedDependReferenceIndexId, true,
								  NULL, 2, fc_key);

		while ((fc_tuple = systable_getnext(fc_scan)) != NULL)
		{
			Form_pg_shdepend fc_sdepForm = (Form_pg_shdepend) GETSTRUCT(fc_tuple);
			MemoryContext fc_cxt,
						fc_oldcxt;

			/*
			 * 我们仅在共享对象和当前数据库中的对象
			 * 上操作
			 */
			if (fc_sdepForm->dbid != MyDatabaseId &&
				fc_sdepForm->dbid != InvalidOid)
				continue;

			/* 我们不干涉非所有者依赖关系 */
			if (fc_sdepForm->deptype != SHARED_DEPENDENCY_OWNER)
				continue;

			/*
			 * 各种 ALTER OWNER 例程倾向于在
			 * CurrentMemoryContext 中泄漏内存。当它们
			 * 每个命令只调用一次时，这不是问题；但在这种用法中，我们可能会
			 * 处理许多对象，这可能导致严重的内存泄漏。
			 * 通过在短生命周期的上下文中运行每个调用来修复它。
			 */
			fc_cxt = AllocSetContextCreate(CurrentMemoryContext,
										"shdepReassignOwned",
										ALLOCSET_DEFAULT_SIZES);
			fc_oldcxt = MemoryContextSwitchTo(fc_cxt);

			/* 发出适当的 ALTER OWNER 调用 */
			switch (fc_sdepForm->classid)
			{
				case TypeRelationId:
					AlterTypeOwner_oid(fc_sdepForm->objid, fc_newrole, true);
					break;

				case NamespaceRelationId:
					AlterSchemaOwner_oid(fc_sdepForm->objid, fc_newrole);
					break;

				case RelationRelationId:

					
					ATExecChangeOwner(fc_sdepForm->objid, fc_newrole, true, AccessExclusiveLock);
					break;

				case DefaultAclRelationId:

					/*
					 * 忽略默认ACL；它们应该通过DROP
					 * OWNED处理，而不是REASSIGN OWNED。
					 */
					break;

				case UserMappingRelationId:
					/* 同上 */
					break;

				case ForeignServerRelationId:
					AlterForeignServerOwner_oid(fc_sdepForm->objid, fc_newrole);
					break;

				case ForeignDataWrapperRelationId:
					AlterForeignDataWrapperOwner_oid(fc_sdepForm->objid, fc_newrole);
					break;

				case EventTriggerRelationId:
					AlterEventTriggerOwner_oid(fc_sdepForm->objid, fc_newrole);
					break;

				case PublicationRelationId:
					AlterPublicationOwner_oid(fc_sdepForm->objid, fc_newrole);
					break;

				case SubscriptionRelationId:
					AlterSubscriptionOwner_oid(fc_sdepForm->objid, fc_newrole);
					break;

					/* 通用修改所有者情况 */
				case CollationRelationId:
				case ConversionRelationId:
				case OperatorRelationId:
				case ProcedureRelationId:
				case LanguageRelationId:
				case LargeObjectRelationId:
				case OperatorFamilyRelationId:
				case OperatorClassRelationId:
				case ExtensionRelationId:
				case StatisticExtRelationId:
				case TableSpaceRelationId:
				case DatabaseRelationId:
				case TSConfigRelationId:
				case TSDictionaryRelationId:
					{
						Oid			fc_classId = fc_sdepForm->classid;
						Relation	fc_catalog;

						if (fc_classId == LargeObjectRelationId)
							fc_classId = LargeObjectMetadataRelationId;

						fc_catalog = table_open(fc_classId, RowExclusiveLock);

						AlterObjectOwner_internal(fc_catalog, fc_sdepForm->objid,
												  fc_newrole);

						table_close(fc_catalog, NoLock);
					}
					break;

				default:
					elog(ERROR, "unexpected classid %u", fc_sdepForm->classid);
					break;
			}

			/* 清理 */
			MemoryContextSwitchTo(fc_oldcxt);
			MemoryContextDelete(fc_cxt);

			/* 确保下一个迭代会看到我的更改 */
			CommandCounterIncrement();
		}

		systable_endscan(fc_scan);
	}

	table_close(fc_sdepRel, RowExclusiveLock);
}
